#!/usr/bin/env python

"""Ninja toolchain abstraction for Android platform"""

import os
import subprocess

import toolchain


def make_target(toolchain, host, target):
    return Android(toolchain, host, target)


class Android(object):
    def __init__(self, toolchain, host, target):
        self.host = host

        if host.is_windows():
            self.exe_suffix = ".exe"
        else:
            self.exe_suffix = ""

        self.javaccmd = (
            toolchain.mkdircmd("$outpath")
            + " && $javac -d $outpath -classpath $outpath -sourcepath $sourcepath -target 1.5 -bootclasspath $androidjar -g -source 1.5 -Xlint:-options $in"
        )
        self.dexcmd = "$dex --dex --output $out $in"
        self.aaptcmd = (
            toolchain.cdcmd("$apkbuildpath")
            + " && $aapt p -f -m -M AndroidManifest.xml -F $apk -I $androidjar -S res --debug-mode --no-crunch -J gen $aaptflags"
        )
        self.aaptdeploycmd = (
            toolchain.cdcmd("$apkbuildpath")
            + " && $aapt c -S res -C bin/res && $aapt p -f -m -M AndroidManifest.xml -F $apk -I $androidjar -S bin/res -S res -J gen $aaptflags"
        )
        self.aaptaddcmd = (
            toolchain.cdcmd("$apkbuildpath")
            + " && "
            + toolchain.copycmd("$apksource", "$apk")
            + " && $aapt a $apk $apkaddfiles"
        )
        self.zipcmd = "$zip -r -9 $out $in $implicitin"
        self.zipaligncmd = "$zipalign -f 4 $in $out"
        self.codesigncmd = "build/ninja/codesign.py --target $target --prefs codesign.json --zipfile $in --config $config --jarsigner $jarsigner $out"

        if host.is_windows():
            self.codesigncmd = "python " + self.codesigncmd

    def initialize_toolchain(self):
        self.ndkpath = os.getenv("NDK_HOME", "")
        self.sdkpath = os.getenv("ANDROID_HOME", "")
        self.sysroot = ""
        self.platformversion = "21"
        self.gcc_toolchainversion = "4.9"
        self.javasdk = ""

        self.archname = dict()
        self.archname["x86"] = "x86"
        self.archname["x86-64"] = "x86_64"
        self.archname["arm6"] = "arm"
        self.archname["arm7"] = "arm"
        self.archname["arm64"] = "arm64"
        self.archname["mips"] = "mips"
        self.archname["mips64"] = "mips64"

        self.archpath = dict()
        self.archpath["x86"] = "x86"
        self.archpath["x86-64"] = "x86-64"
        self.archpath["arm6"] = "armeabi"
        self.archpath["arm7"] = "armeabi-v7a"
        self.archpath["arm64"] = "arm64-v8a"
        self.archpath["mips"] = "mips"
        self.archpath["mips64"] = "mips64"

        self.gcc_toolchainname = dict()
        self.gcc_toolchainname["x86"] = "x86-" + self.gcc_toolchainversion
        self.gcc_toolchainname["x86-64"] = "x86_64-" + self.gcc_toolchainversion
        self.gcc_toolchainname["arm6"] = (
            "arm-linux-androideabi-" + self.gcc_toolchainversion
        )
        self.gcc_toolchainname["arm7"] = (
            "arm-linux-androideabi-" + self.gcc_toolchainversion
        )
        self.gcc_toolchainname["arm64"] = (
            "aarch64-linux-android-" + self.gcc_toolchainversion
        )
        self.gcc_toolchainname["mips"] = (
            "mipsel-linux-android-" + self.gcc_toolchainversion
        )
        self.gcc_toolchainname["mips64"] = (
            "mips64el-linux-android-" + self.gcc_toolchainversion
        )

        self.gcc_toolchainprefix = dict()
        self.gcc_toolchainprefix["x86"] = "i686-linux-android-"
        self.gcc_toolchainprefix["x86-64"] = "x86_64-linux-android-"
        self.gcc_toolchainprefix["arm6"] = "arm-linux-androideabi-"
        self.gcc_toolchainprefix["arm7"] = "arm-linux-androideabi-"
        self.gcc_toolchainprefix["arm64"] = "aarch64-linux-android-"
        self.gcc_toolchainprefix["mips"] = "mipsel-linux-android-"
        self.gcc_toolchainprefix["mips64"] = "mips64el-linux-android-"

        if self.host.is_windows():
            if os.getenv("PROCESSOR_ARCHITECTURE", "AMD64").find("64") != -1:
                self.hostarchname = "windows-x86_64"
            else:
                self.hostarchname = "windows-x86"
        elif self.host.is_linux():
            localarch = toolchain.check_output(["uname", "-m"])
            if localarch == "x86_64":
                self.hostarchname = "linux-x86_64"
            else:
                self.hostarchname = "linux-x86"
        elif self.host.is_macos():
            self.hostarchname = "darwin-x86_64"

    def build_toolchain(self):
        buildtools_path = os.path.join(self.sdkpath, "build-tools")
        buildtools_list = [
            item
            for item in os.listdir(buildtools_path)
            if os.path.isdir(os.path.join(buildtools_path, item))
        ]
        buildtools_list.sort(key=lambda s: map(int, s.split("-")[0].split(".")))

        self.buildtools_path = os.path.join(
            self.sdkpath, "build-tools", buildtools_list[-1]
        )
        self.android_jar = os.path.join(
            self.sdkpath, "platforms", "android-" + self.platformversion, "android.jar"
        )

        self.javac = "javac"
        self.jarsigner = "jarsigner"
        if self.javasdk != "":
            self.javac = os.path.join(self.javasdk, "bin", self.javac)
            self.jarsigner = os.path.join(self.javasdk, "bin", self.jarsigner)
        if self.host.is_windows():
            self.dex = os.path.join(self.buildtools_path, "dx.bat")
        else:
            self.dex = os.path.join(self.buildtools_path, "dx" + self.exe_suffix)
        if not os.path.isfile(self.dex):
            self.dex = os.path.join(self.sdkpath, "tools", "dx" + self.exe_suffix)
        self.aapt = os.path.join(self.buildtools_path, "aapt" + self.exe_suffix)
        self.zipalign = os.path.join(self.buildtools_path, "zipalign" + self.exe_suffix)
        if not os.path.isfile(self.zipalign):
            self.zipalign = os.path.join(
                self.sdkpath, "tools", "zipalign" + self.exe_suffix
            )

    def parse_prefs(self, prefs):
        if "android" in prefs:
            androidprefs = prefs["android"]
            if "ndkpath" in androidprefs:
                self.ndkpath = os.path.expanduser(androidprefs["ndkpath"])
            if "sdkpath" in androidprefs:
                self.sdkpath = os.path.expanduser(androidprefs["sdkpath"])
            if "platformversion" in androidprefs:
                self.platformversion = androidprefs["platformversion"]
            if "gccversion" in androidprefs:
                self.gcc_toolchainversion = androidprefs["gccversion"]
            if "javasdk" in androidprefs:
                self.javasdk = androidprefs["javasdk"]

    def write_variables(self, writer):
        writer.variable("ndk", self.ndkpath)
        writer.variable("sdk", self.sdkpath)
        writer.variable("sysroot", self.sysroot)
        writer.variable("androidjar", self.android_jar)
        writer.variable("apkbuildpath", "")
        writer.variable("apk", "")
        writer.variable("apksource", "")
        writer.variable("apkaddfiles", "")
        writer.variable("javac", self.javac)
        writer.variable("dex", self.dex)
        writer.variable("aapt", self.aapt)
        writer.variable("zipalign", self.zipalign)
        writer.variable("jarsigner", self.jarsigner)
        writer.variable("aaptflags", "")

    def write_rules(self, writer):
        writer.rule("aapt", command=self.aaptcmd, description="AAPT $out")
        writer.rule("aaptdeploy", command=self.aaptdeploycmd, description="AAPT $out")
        writer.rule("aaptadd", command=self.aaptaddcmd, description="AAPT $out")
        writer.rule("javac", command=self.javaccmd, description="JAVAC $in")
        writer.rule("dex", command=self.dexcmd, description="DEX $out")
        writer.rule("zip", command=self.zipcmd, description="ZIP $out")
        writer.rule("zipalign", command=self.zipaligncmd, description="ZIPALIGN $out")
        writer.rule("codesign", command=self.codesigncmd, description="CODESIGN $out")

    def make_sysroot_path(self, arch):
        return os.path.join(
            self.ndkpath,
            "platforms",
            "android-" + self.platformversion,
            "arch-" + self.archname[arch],
        )

    def make_gcc_toolchain_path(self, arch):
        return os.path.join(
            self.ndkpath,
            "toolchains",
            self.gcc_toolchainname[arch],
            "prebuilt",
            self.hostarchname,
        )

    def make_gcc_bin_path(self, arch):
        return os.path.join(
            self.make_gcc_toolchain_path(arch), "bin", self.gcc_toolchainprefix[arch]
        )

    def archname(self):
        return self.archname

    def archpath(self):
        return self.archpath

    def hostarchname(self):
        return self.hostarchname

    def apk(
        self,
        toolchain,
        writer,
        module,
        archbins,
        javasources,
        outpath,
        binname,
        basepath,
        config,
        implicit_deps,
        resources,
    ):
        buildpath = os.path.join("$buildpath", config, "apk", binname)
        baseapkname = binname + ".base.apk"
        unsignedapkname = binname + ".unsigned.apk"
        unalignedapkname = binname + ".unaligned.apk"
        apkname = binname + ".apk"
        apkfiles = []
        libfiles = []
        locallibs = []
        resfiles = []
        manifestfile = []

        writer.comment("Make APK")
        for _, value in archbins.iteritems():
            for archbin in value:
                archpair = os.path.split(archbin)
                libname = archpair[1]
                arch = os.path.split(archpair[0])[1]
                locallibpath = os.path.join("lib", self.archpath[arch], libname)
                archpath = os.path.join(buildpath, locallibpath)
                locallibs += [locallibpath + " "]
                libfiles += toolchain.copy(writer, archbin, archpath)
        for resource in resources:
            filename = os.path.split(resource)[1]
            if filename == "AndroidManifest.xml":
                manifestfile = toolchain.copy(
                    writer,
                    os.path.join(basepath, module, resource),
                    os.path.join(buildpath, "AndroidManifest.xml"),
                )
            else:
                restype = os.path.split(os.path.split(resource)[0])[1]
                if restype == "asset":
                    pass  # todo: implement
                else:
                    resfiles += toolchain.copy(
                        writer,
                        os.path.join(basepath, module, resource),
                        os.path.join(buildpath, "res", restype, filename),
                    )

        # Make directories
        gendir = toolchain.mkdir(writer, os.path.join(buildpath, "gen"))
        bindir = toolchain.mkdir(writer, os.path.join(buildpath, "bin"))
        binresdir = toolchain.mkdir(
            writer, os.path.join(buildpath, "bin", "res"), order_only=bindir
        )
        alldirs = gendir + bindir + binresdir

        aaptvars = [("apkbuildpath", buildpath), ("apk", baseapkname)]
        aaptout = os.path.join(buildpath, baseapkname)
        if config == "deploy":
            baseapkfile = writer.build(
                aaptout,
                "aaptdeploy",
                manifestfile,
                variables=aaptvars,
                implicit=manifestfile + resfiles,
                order_only=alldirs,
            )
        else:
            baseapkfile = writer.build(
                aaptout,
                "aapt",
                manifestfile,
                variables=aaptvars,
                implicit=manifestfile + resfiles,
                order_only=alldirs,
            )

        # Compile java code
        javafiles = []
        localjava = []
        if javasources != []:
            # self.javaccmd = '$javac -d $outpath -classpath $outpath -sourcepath $sourcepath -target 1.5 -bootclasspath $androidjar -g -source 1.5 -Xlint:-options $in'
            # self.dexcmd = '$dex --dex --output $out $in'
            javasourcepath = "."
            if self.host.is_windows():
                javasourcepath += ";"
            else:
                javasourcepath += ":"
            javasourcepath += os.path.join(buildpath, "gen")
            classpath = os.path.join(buildpath, "classes")
            javavars = [("outpath", classpath), ("sourcepath", javasourcepath)]
            javaclasses = writer.build(
                classpath,
                "javac",
                javasources,
                variables=javavars,
                implicit=baseapkfile,
            )
            localjava += ["classes.dex"]
            javafiles += writer.build(
                os.path.join(buildpath, "classes.dex"), "dex", classpath
            )

        # Add native libraries and java classes to apk
        aaptvars = [
            ("apkbuildpath", buildpath),
            ("apk", unsignedapkname),
            ("apksource", baseapkname),
            ("apkaddfiles", toolchain.paths_forward_slash(locallibs + localjava)),
        ]
        unsignedapkfile = writer.build(
            os.path.join(buildpath, unsignedapkname),
            "aaptadd",
            baseapkfile,
            variables=aaptvars,
            implicit=libfiles + javafiles,
            order_only=alldirs,
        )

        # Sign the APK
        codesignvars = [("config", config)]
        unalignedapkfile = writer.build(
            os.path.join(buildpath, unalignedapkname),
            "codesign",
            unsignedapkfile,
            variables=codesignvars,
        )

        # Run zipalign
        outfile = writer.build(
            os.path.join(outpath, config, apkname), "zipalign", unalignedapkfile
        )
        return outfile
